package org.eclipse.gittab.player.internal.midi;

import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Synthesizer;

import org.eclipse.gittab.gitTabDsl.GuitarSound;
import org.eclipse.gittab.gitTabDsl.Note;
import org.eclipse.gittab.gitTabDsl.Tuning;
import org.eclipse.gittab.player.SimpleNotePlayer.NotePlayer;

public class NotePlayerImpl implements NotePlayer {

	private Note note;

	private Tuning tuning;

	private int sec;

	private GuitarSound guitar;

	private Synthesizer synthesizer;

	public NotePlayerImpl() {

		try {
			synthesizer = MidiSystem.getSynthesizer();
			synthesizer.open();
		} catch (MidiUnavailableException e) {
		}
	}

	public NotePlayer play(Note note) {
		this.note = note;
		return this;
	}

	@Override
	public NotePlayer seconds(int sec) {
		this.sec = sec;
		checkAndPlay();
		return this;
	}

	@Override
	public NotePlayer use(Tuning tuning) {
		this.tuning = tuning;
		checkAndPlay();
		return this;
	}

	@Override
	public NotePlayer with(GuitarSound guitar) {
		this.guitar = guitar;
		checkAndPlay();
		return this;
	}

	/**
	 * Plays a single note with the given guitar sound and the length in
	 * seconds.
	 * 
	 */
	private void checkAndPlay() {

		if (tuning != null && guitar != null && sec > 0 && sec < 10) {

			/*
			 * Turn the note on on MIDI channel 1. (Index zero means MIDI
			 * channel 1)
			 */
			MidiChannel[] channels = synthesizer.getChannels();
			MidiChannel channel = channels[0];
			channel.programChange(MidiUtil.guitarToMidi(guitar));
			int midiNr = MidiMapper.TypeRegistry.CACHE
					.getMidiFrom(tuning, note);
			channel.noteOn(midiNr, 93);

			/*
			 * Wait for the specified amount of time (the duration of the note).
			 */
			try {
				Thread.sleep(sec * 1000);
			} catch (InterruptedException e) {
			}

			/*
			 * Turn the note off.
			 */
			channel.noteOff(midiNr);

			/*
			 * Close the synthesizer.
			 */
			synthesizer.close();
		}
	}
}